/**
 * View package.
 */
package fr.umlv.m2.ig.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.swing.JLayeredPane;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import fr.umlv.m2.ig.handler.XMLColorHandler;
import fr.umlv.m2.ig.model.BeauheurModelImpl;
import fr.umlv.m2.ig.model.Journey;
import fr.umlv.m2.ig.model.Line;
import fr.umlv.m2.ig.model.Station;
import fr.umlv.m2.ig.model.StationState;
import fr.umlv.m2.ig.model.StationType;
import fr.umlv.m2.ig.model.Train;
import fr.umlv.m2.ig.model.event.StationEvent;
import fr.umlv.m2.ig.model.event.StationListener;
import fr.umlv.m2.ig.model.event.TrainEvent;
import fr.umlv.m2.ig.model.event.TrainListener;

/**
 * This class defines the map view containing a {@code JLayeredPane}
 * which will draw subway map and trains.
 * 
 * @author Gambouele Bertrand.
 * @author Viravout Alexandre.
 * 
 */
public class SubwayMapView {

	/**
	 * Model which manages lines, stations and trains.
	 */
	private final BeauheurModelImpl model;

	/**
	 * The layered pane containing map and trains panels.
	 */
	private final JLayeredPane mainPanel;

	/**
	 * Pane containing image in which map will be drawn.
	 */
	private final ImagePanel mapPanel;

	/**
	 * Pane in which trains will be drawn.
	 */
	private final JPanel trainsPanel;

	/**
	 * Map of colors for each subway line.
	 */
	private final Map<String, Color> colors;

	/**
	 * Top-left corner of the image portion which is currently drawn.
	 */
	private final Point topLeftC;

	/**
	 * Image in which the subway map is drawn.
	 */
	private final BufferedImage mapImage;

	/**
	 * Shape of each station in the map image.
	 */
	private final Set<StationShape> stationsShapes;

	/**
	 * Radius of stations in the map image.
	 */
	private final int radius;

	/**
	 * The selected line.
	 */
	private Line selectedLine;

	/**
	 * Value of the current scale factor.
	 */
	private int zoom;

	/**
	 * Boolean indicating if shapes have already been initialized.
	 */
	private boolean shapesInitialized;
	
	/**
	 * A boolean used for string drawing.
	 */
	private boolean drawUp;
	
	/**
	 * Set of stations already drawn.
	 */
	private final Set<String> drawn;
	
	/**
	 * Set of stations present on more than one line to draw.
	 */
	private final Set<Station> multiLinesStations;
	
	/**
	 * Array of x coordinates to draw stations served by many lines.
	 */
	private final List<Integer> xArray;
	
	/**
	 * Array of y coordinates to draw stations served by many lines.
	 */
	private final List<Integer> yArray;
	
	/**
	 * Trains components.
	 */
	private final Map<String, TrainComponent> trainsComponents;
	
	/**
	 * Handler to update the top left corner location after a zoom or a drag.
	 */
	private final ZoomAndDragHandler zoomAndDragHandler;

	/**
	 * Default stroke used for drawing.
	 */
	private BasicStroke defaultStroke;

	/**
	 * Stroke used to draw closed stations.
	 */
	private Stroke closedStationStroke;
	
	/**
	 * Creates a new {@code SubwayMapView} with the specified model.
	 * 
	 * @param model the based model.
	 * @throws SAXException
	 * if an exception occurs while loading colors from the XML file.
	 * @throws IOException
	 * if an IOException occurs while reading colors file.
	 */
	public SubwayMapView(final BeauheurModelImpl model)
	throws SAXException, IOException {
		this.mainPanel = new JLayeredPane();
		this.topLeftC = new Point();
		this.radius = 3;
		this.zoom = 1;
		this.selectedLine = null;
		this.shapesInitialized = false;
		this.drawUp = true;
		this.stationsShapes = new HashSet<StationShape>();
		this.drawn = new HashSet<String>();
		this.multiLinesStations = new HashSet<Station>();
		this.xArray = new LinkedList<Integer>();
		this.yArray = new LinkedList<Integer>();
		this.model = model;
		this.zoomAndDragHandler = new ZoomAndDragHandler(this.zoom, model.getMapWidth(), model.getMapHeight());
		this.colors = this.initColors();
		this.mapImage = new BufferedImage(model.getMapWidth(), model.getMapHeight(), BufferedImage.TYPE_3BYTE_BGR);
		this.mapPanel = this.initMapPanel();
		this.trainsPanel = this.initTrainsPanel();
		this.trainsComponents = new HashMap<String, TrainComponent>();

		this.initLayeredPane();
		this.registerToModel();
	}

	/**
	 * Registers the map view into the model.
	 */
	private void registerToModel() {
		this.model.addStationListener(new StationListener() {
			
			@Override
			public void stateChanged(StationEvent event) {
				drawMapImage();
				mapPanel.repaint();
			}
		});
		
		this.model.addTrainListener(new TrainListener() {
			
			@Override
			public void trainsMoved(TrainEvent event) {
				for (Train train : event.getMovedTrains()) {
					int posX = (int) ((train.getPositionX() - topLeftC.x - radius) * zoom);
					int posY = (int) ((train.getPositionY() - topLeftC.y - radius) * zoom);
					TrainComponent tc = trainsComponents.get(train.getId());
					
					tc.setLocation(posX, posY);
				}

				trainsPanel.repaint();
			}
			
			@Override
			public void trainRemoved(TrainEvent event) {
				TrainComponent tc = trainsComponents.remove(event.getTrain().getId());
				trainsPanel.remove(tc);
				trainsPanel.repaint();
			}
			
			@Override
			public void trainAdded(TrainEvent event) {
				final TrainComponent tc = new TrainComponent(event.getTrain());
				int posX = (int) ((event.getTrain().getPositionX() - topLeftC.x - radius) * zoom);
				int posY = (int) ((event.getTrain().getPositionY() - topLeftC.y - radius) * zoom);
				
				tc.setLocation(posX, posY);
				tc.setSize(radius*2*zoom, radius*2*zoom);
				tc.setOpaque(false);
				
				trainsComponents.put(event.getTrain().getId(), tc);
				trainsPanel.add(tc);
				
				tc.addMouseListener(new MouseAdapter() {
					@Override
					public void mouseClicked(MouseEvent e) {
						if (e.getButton() == MouseEvent.BUTTON3) {
							model.removeTrain(tc.getTrain());
						}
					}
				});
			}

			@Override
			public void trainsListReseted(TrainEvent event) {
				trainsComponents.clear();
				trainsPanel.removeAll();
				trainsPanel.repaint();
			}
		});
	}

	/**
	 * Creates a layout for the {@code JLayeredPane} and adds map panel and
	 * trains panel.
	 */
	private void initLayeredPane() {
		this.mainPanel.add(mapPanel, Integer.valueOf(0));
		this.mainPanel.add(trainsPanel, Integer.valueOf(1));
		
		this.mainPanel.setLayout(new LayoutManager() {
			private final Dimension size = new Dimension();

			@Override
			public void removeLayoutComponent(Component c) {
			}

			@Override
			public Dimension preferredLayoutSize(Container parent) {
				Component[] components = parent.getComponents();

				if (components.length == 0) {
					size.setSize(0, 0);
				} else {
					size.setSize(components[0].getPreferredSize());
					
					for (int i = 1; i < components.length; i++) {
						Dimension d = components[i].getPreferredSize();
						
						if (size.width < d.width) {
							size.width = d.width;
						}
						
						if (size.height < d.height) {
							size.height = d.height;
						}
					}
				}

				return size;
			}

			@Override
			public Dimension minimumLayoutSize(Container parent) {
				Component[] components = parent.getComponents();

				if (components.length == 0) {
					size.setSize(0, 0);
				} else {
					size.setSize(components[0].getMinimumSize());
					
					for (int i = 1; i < components.length; i++) {
						Dimension d = components[i].getMinimumSize();
						
						if (size.width < d.width) {
							size.width = d.width;
						}
						
						if (size.height < d.height) {
							size.height = d.height;
						}
					}
				}

				return size;
			}

			@Override
			public void layoutContainer(Container parent) {
				Dimension d = parent.getSize();

				for (Component c : parent.getComponents()) {
					c.setSize(d);
				}
			}

			@Override
			public void addLayoutComponent(String name, Component c) {
			}
		});
	}

	/**
	 * Creates a new transparent {@code JPanel}.
	 * 
	 * @return A new transparent {@code JPanel}.
	 */
	private JPanel initTrainsPanel() {
		JPanel panel = new JPanel();
		
		panel.setPreferredSize(new Dimension(model.getMapWidth(), model.getMapHeight()));
		panel.setOpaque(false);
		panel.setLayout(null);

		return panel;
	}
	
	/**
	 * Initializes the map panel.
	 * 
	 * @return A new {@code JPanel} in which map is drawn.
	 */
	private ImagePanel initMapPanel() {
		drawMapImage();

		final ImagePanel panel = new ImagePanel();
		
		panel.setPreferredSize(new Dimension(model.getMapWidth(), model.getMapHeight()));
		panel.setImage(mapImage);

		panel.addMouseListener(new MouseAdapter() {
			
			/**
			 * Coordinates of the top left corner on last mouse pressed event.
			 */
			private final Point lastCorner = new Point(0,0);
			
			/**
			 * Mouse coordinates on last mouse pressed event.
			 */
			private final Point lastMouseCoord = new Point(0,0);
			
			/**
			 * List of clicked stations.
			 */
			private final List<StationShape> clickedStations = new ArrayList<StationShape>();
			
			/**
			 * Handler for mouse motion events when mouse is pressed.
			 */
			private final MouseMotionAdapter dragListener = new MouseMotionAdapter() {
				
				@Override
				public void mouseDragged(MouseEvent e) {
					drawMapImage();
					mapPanel.repaint();
					
					if (zoomAndDragHandler.performDragWithMouse(lastCorner, topLeftC, lastMouseCoord, e.getPoint())) {
						
						if (!trainsComponents.isEmpty()) {
							for (TrainComponent tc : trainsComponents.values()) {
								int posX = (int) (((int)tc.getTrain().getPositionX() - topLeftC.x - radius) * zoom);
								int posY = (int) (((int)tc.getTrain().getPositionY() - topLeftC.y - radius) * zoom);
								
								tc.setLocation((posX-topLeftC.x)*zoom, (posY-topLeftC.y)*zoom);
							}
							
							trainsPanel.repaint();
						}
					}
				}
			};
			
			
			@Override
			public void mousePressed(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON1) {
					lastMouseCoord.setLocation(e.getPoint());
					lastCorner.setLocation(topLeftC);
					panel.addMouseMotionListener(dragListener);
				}
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON1) {
					panel.removeMouseMotionListener(dragListener);
				}
			}

			@Override
			public void mouseClicked(final MouseEvent e) {
				int x = (e.getPoint().x / zoom) + topLeftC.x;
				int y = (e.getPoint().y / zoom) + topLeftC.y;
				
				for (StationShape shape : stationsShapes) {
					if (shape.getShape().contains(x, y)) {
						clickedStations.add(shape);
					}
				}
				
				final Line lastLine = selectedLine;
				selectedLine = null;
				
				if (clickedStations.size() > 1) {
					JPopupMenu pm = new JPopupMenu();
					
					for (final StationShape ss : clickedStations) {
						JMenuItem mi = new JMenuItem("Ligne " + ss.getLine());
						mi.addActionListener(new ActionListener() {
							
							@Override
							public void actionPerformed(ActionEvent event) {
								if (e.getButton() == MouseEvent.BUTTON1) {
									selectedLine = model.getLines().get(ss.getLine());
									
									if (lastLine != null && lastLine != selectedLine) {
										selectedLine = null;
									}
									
									if (lastLine != selectedLine) {
										TrainComponent.selectedLine = (selectedLine == null) ? null : selectedLine.getName();
										
										drawMapImage();
										panel.repaint();
									}
								} else if (e.getButton() == MouseEvent.BUTTON3) {
									model.changeStationState(ss.getStation(), ss.getLine());
								}
							}
						});
						
						pm.add(mi);
					}
					
					pm.show(mapPanel, e.getPoint().x, e.getPoint().y);
				} else if (clickedStations.size() == 1) {
					StationShape ss = clickedStations.get(0);
					
					if (e.getButton() == MouseEvent.BUTTON1) {
						Line last = selectedLine;
						selectedLine = model.getLines().get(ss.getLine());
						
						if (last != null && last != selectedLine) {
							selectedLine = null;
						}
						
						if (last != selectedLine) {
							TrainComponent.selectedLine = (selectedLine == null) ? null : selectedLine.getName();

							drawMapImage();
							panel.repaint();
						}
					} else if (e.getButton() == MouseEvent.BUTTON3) {
						model.changeStationState(ss.getStation(), ss.getLine());
					}
				}
				
				clickedStations.clear();

				if (lastLine != selectedLine) {
					TrainComponent.selectedLine = (selectedLine == null) ? null : selectedLine.getName();

					drawMapImage();
					panel.repaint();
				}
			}
		});

		panel.addMouseWheelListener(new MouseWheelListener() {
			
			@Override
			public void mouseWheelMoved(MouseWheelEvent e) {
				int newZoom = zoom;
				
				if (e.getWheelRotation() < 0){
					newZoom = zoomAndDragHandler.performZoomInWithMouse(topLeftC, e.getPoint());
				} else if (e.getWheelRotation() > 0) {
					newZoom = zoomAndDragHandler.performZoomOutWithMouse(topLeftC, e.getPoint());
				}

				if (newZoom != zoom) {
					zoom = newZoom;
					
					if (!trainsComponents.isEmpty()) {
						for (TrainComponent tc : trainsComponents.values()) {
							int posX = (int) (((int)tc.getTrain().getPositionX() - topLeftC.x - radius) * zoom);
							int posY = (int) (((int)tc.getTrain().getPositionY() - topLeftC.y - radius) * zoom);
							
							tc.setLocation((posX-topLeftC.x)*zoom, (posY-topLeftC.y)*zoom);
							tc.setSize(zoom*2*radius, zoom*2*radius);
						}
					}
					
					drawMapImage();
					mapPanel.repaint();
				}
			}
		});

		panel.setOpaque(true);
		shapesInitialized = true;

		return panel;
	}

	/**
	 * Loads colors from a XML file.
	 * 
	 * @return A map containing {@code Color} objects for each line.
	 * @throws SAXException if a error occurs during parser initialization.
	 * @throws IOException if a error occurs while reading file.
	 */
	private Map<String, Color> initColors() throws SAXException, IOException {
		XMLColorHandler handler = new XMLColorHandler();
		XMLReader reader = XMLReaderFactory.createXMLReader();
		
		reader.setContentHandler(handler);

		InputStream colorsFile = this.getClass().getResourceAsStream("/colors.xml");
		InputSource source = new InputSource(colorsFile);

		try {
			reader.parse(source);
		} finally {
			colorsFile.close();
		}

		return handler.getColors();
	}

	/**
	 * Draws subway map in a buffered image.
	 */
	private void drawMapImage() {
		Graphics2D g = mapImage.createGraphics();
		defaultStroke = new BasicStroke(radius * zoom, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
		closedStationStroke = new BasicStroke(1.1f * zoom, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

		try {
			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g.setStroke(defaultStroke);
			g.setFont(new Font("dialog", Font.PLAIN, 3*zoom));
			g.setBackground(colors.get("background"));
			g.clearRect(0, 0, mapImage.getWidth(), mapImage.getHeight());

			if (selectedLine == null) {
				for (Entry<String, Line> line : model.getLines().entrySet()) {
					drawLine(line.getValue(), g);
					drawn.clear();
				}
			} else {
				drawLine(selectedLine, g);
				drawn.clear();
			}
			
			drawMultiLineStations(g);

		} finally {
			g.dispose();
		}
	}

	/**
	 * Draw stations served by many lines.
	 * @param g the graphics to be used.
	 */
	private void drawMultiLineStations(final Graphics2D g) {
		Color lastColor = g.getColor();
		Color inColor = colors.get("intersect_bg");
		Color outColor = colors.get("intersect_fg");
		
		g.setStroke(closedStationStroke);
		
		if (selectedLine != null) {
			for (Station station : multiLinesStations) {
				if (station.isServed(selectedLine.getName())) {
					Point p = station.getCoordinates(selectedLine.getName());
					
					g.setColor(inColor);
					g.fillOval((p.x - radius - topLeftC.x) * zoom,
							(p.y - radius - topLeftC.y) * zoom,
							2 * radius * zoom, 2 * radius * zoom);
					
					g.setColor(outColor);
					g.drawOval((p.x - radius - topLeftC.x) * zoom,
							(p.y - radius - topLeftC.y) * zoom,
							2 * radius * zoom, 2 * radius * zoom);
					
					if (station.getState(selectedLine.getName()) == StationState.CLOSED) {
						int a1 = (p.x - radius - topLeftC.x) * zoom;
						int b1 = (p.y - radius - topLeftC.y) * zoom;
						int a2 = (p.x + radius - topLeftC.x) * zoom;
						int b2 = (p.y + radius - topLeftC.y) * zoom;
						Color oldColor = g.getColor();
						
						g.setColor(colors.get("closed"));
						g.drawLine(a1, b1, a2, b2);
						
						int tmp = a1;
						a1 = a2;
						a2 = tmp;
						
						g.drawLine(a1, b1, a2, b2);
						g.setColor(oldColor);
					}
				}
			}
		} else {
			for (Station station : multiLinesStations) {
				xArray.clear();
				yArray.clear();
				
				Object[] lines = station.getLines().toArray();
				int nbLines = lines.length;
				int x1 = 0;
				int y1 = 0;
				
				if (nbLines > 0) {
					Point p = station.getCoordinates((String)lines[0]);
					x1 = p.x;
					y1 = p.y;
					xArray.add(x1);
					yArray.add(y1);
				}
				
				g.setColor(outColor);
				
				for (int i = 1; i < nbLines; i++) {
					Point p = station.getCoordinates((String)lines[i]);
					int x2 = p.x;
					int y2 = p.y;
					
					xArray.add(x2);
					yArray.add(y2);
					g.drawLine((x1-topLeftC.x)*zoom, (y1-topLeftC.y)*zoom, (x2-topLeftC.x)*zoom, (y2-topLeftC.y)*zoom);
					
					x1 = x2;
					y1 = y2;
				}
				
				Iterator<Integer> itX = xArray.iterator();
				Iterator<Integer> itY = yArray.iterator();
				
				while (itX.hasNext() && itY.hasNext()) {
					x1 = (Integer) itX.next();
					y1 = (Integer) itY.next();
					
					g.drawOval((x1 - radius - topLeftC.x) * zoom,
							(y1 - radius - topLeftC.y) * zoom,
							2 * radius * zoom, 2 * radius * zoom);
				}
				
				itX = xArray.iterator();
				itY = yArray.iterator();

				for (int i = 0; i < nbLines && itX.hasNext() && itY.hasNext(); i++) {
					x1 = (Integer) itX.next();
					y1 = (Integer) itY.next();
					
					g.setColor(inColor);
					g.fillOval((x1 - radius - topLeftC.x) * zoom,
							(y1 - radius - topLeftC.y) * zoom,
							2 * radius * zoom, 2 * radius * zoom);
					
					if (station.getState((String)lines[i]) == StationState.CLOSED) {
						int a1 = (x1 - radius - topLeftC.x) * zoom;
						int b1 = (y1 - radius - topLeftC.y) * zoom;
						int a2 = (x1 + radius - topLeftC.x) * zoom;
						int b2 = (y1 + radius - topLeftC.y) * zoom;
						Color oldColor = g.getColor();
						
						g.setColor(colors.get("closed"));
						g.drawLine(a1, b1, a2, b2);
						
						int tmp = a1;
						a1 = a2;
						a2 = tmp;
						
						g.drawLine(a1, b1, a2, b2);
						g.setColor(oldColor);
					}
				}
			}
		}
		
		g.setColor(lastColor);
		g.setStroke(defaultStroke);
	}

	/**
	 * Draws a subway line.
	 * 
	 * @param line The line which will be drawn.
	 * @param g the {@code Graphics2D} object to be used for drawing.
	 */
	private void drawLine(final Line line, final Graphics2D g) {
		g.setColor(colors.get(line.getName()));
		drawUp = true;
		
		for (Journey journey : line.getJourneys()) {
			if (!drawn.contains(journey.getOrigin())) {
				Station station = model.getStation(journey.getOrigin());
				drawStation(line.getName(), station, g);
			}
		}
	}

	/**
	 * Draws a subway station.
	 * 
	 * @param line the line of the station.
	 * @param station the station to be drawn.
	 * @param g the {@code Graphics2D} object to be used for drawing.
	 */
	private void drawStation(final String lineName, final Station station, final Graphics2D g) {
		drawn.add(station.getName());

		Point p = station.getCoordinates(lineName);
		Collection<String> nextStations = station.getAllNextStations(lineName);

		for (String nextStation : nextStations) {
			Station next = model.getStation(nextStation);

			if (next != null && (!drawn.contains(next.getName()) || station.getNextStation(lineName, "") != null) ) {
				Point q = next.getCoordinates(lineName);
				g.drawLine(zoom*(p.x-topLeftC.x), zoom*(p.y-topLeftC.y), zoom*(q.x-topLeftC.x), zoom*(q.y-topLeftC.y));
				
				if (!drawn.contains(next.getName())) {
					drawStation(lineName, next, g);
				}
			}
		}
		
		if (station.getType() == StationType.STATION) {
			int countLines = station.countLines();
			
			if (!shapesInitialized && countLines > 1) {
				multiLinesStations.add(station);
			} else if (countLines == 1) {
				g.fillOval((p.x - radius - topLeftC.x) * zoom,
						(p.y - radius - topLeftC.y) * zoom,
						2 * radius * zoom, 2 * radius * zoom);
				
				if (station.getState(lineName) == StationState.CLOSED) {
					int x1 = (p.x - radius - topLeftC.x) * zoom;
					int y1 = (p.y - radius - topLeftC.y) * zoom;
					int x2 = (p.x + radius - topLeftC.x) * zoom;
					int y2 = (p.y + radius - topLeftC.y) * zoom;
					Color lastColor = g.getColor();
					
					g.setColor(colors.get("closed"));
					g.setStroke(closedStationStroke);
					g.drawLine(x1, y1, x2, y2);
					
					int tmp = x1;
					x1 = x2;
					x2 = tmp;
					
					g.drawLine(x1, y1, x2, y2);
					g.setColor(lastColor);
					g.setStroke(defaultStroke);
				}
			}
			
			if (selectedLine != null) {
				Color lastColor = g.getColor();
				g.setColor(colors.get("text"));
				
				int y;
				
				if (drawUp) {
					drawUp = false;
					y = p.y - 2*radius/3 - topLeftC.y;
				} else {
					drawUp = true;
					y = p.y + 5*radius/4 - topLeftC.y;
				}
				
				g.drawString(station.getName(), zoom * (p.x + radius - topLeftC.x), zoom * y);
				g.setColor(lastColor);
			}
			
			if (!shapesInitialized) {
				Collection<String> stationLines = station.getLines();

				for (String line : stationLines) {
					Point coord = station.getCoordinates(line);
					Shape s = new Ellipse2D.Float(coord.x-radius, coord.y-radius, 2 * radius, 2 * radius);
					stationsShapes.add(new StationShape(s, station.getName(), line));
				}
			}
		}
	}

	/**
	 * Returns the {@code JLayeredPane} of the view.
	 * 
	 * @return A {@code JLayeredPane}.
	 */
	public JLayeredPane getPanel() {
		return this.mainPanel;
	}
	
	/**
	 * Gets lines color.
	 * 
	 * @return a map of color.
	 */
	public Map<String, Color> getLinesColor() {
	  return this.colors;
	}
}